home *** CD-ROM | disk | FTP | other *** search
/ Planet Source Code Jumbo …e CD Visual Basic 1 to 7 / 6_2008-2009.ISO / data / zips / Form_Shado214610362009.psc / Form Shadows / clsShadow.cls
Text File  |  2009-03-06  |  59KB  |  1,065 lines

  1. VERSION 1.0 CLASS
  2. BEGIN
  3.   MultiUse = -1  'True
  4.   Persistable = 0  'NotPersistable
  5.   DataBindingBehavior = 0  'vbNone
  6.   DataSourceBehavior  = 0  'vbNone
  7.   MTSTransactionMode  = 0  'NotAnMTSObject
  8. END
  9. Attribute VB_Name = "clsShadow"
  10. Attribute VB_GlobalNameSpace = False
  11. Attribute VB_Creatable = True
  12. Attribute VB_PredeclaredId = False
  13. Attribute VB_Exposed = False
  14. '*************************************************************************************************
  15. '* clsShadow - SelfSub based sample that creates a form shadow.
  16. '*
  17. '* Originally by Paul Caton
  18. '* SelfSub contributions made by LaVolpe and Tai Chi Minh Ralph Eastwood
  19. '* Minor edits and improvements by Joe Jordan (March 6, 2009)
  20. '*
  21. '*
  22. '* Copyright free, use and abuse as you see fit.
  23. '*
  24. '* v1.0 Re-write of the SelfSub/WinSubHook-2 submission to Planet Source Code............ 20060322
  25. '* v1.1 Shadow color property added...................................................... 20060406
  26. '* v1.2 SelfSub updated to LaVolpe's thunks, removed unused code
  27. '*      Added Windows Vista, Windows 7 Support........................................... 20090306
  28. '*************************************************************************************************
  29.  
  30. Option Explicit
  31.  
  32. '------------------------------------------------------------------------------------------------------------------------------------
  33. ' SelfSub Items used in all thunks (SelfHook and SelfCallback have been removed, see URL above for full version)
  34. '------------------------------------------------------------------------------------------------------------------------------------
  35.   ' Local variables/constants
  36.     Private z_scFunk            As Collection   'hWnd/thunk-address collection; initialized as needed
  37.     Private Const IDX_INDEX     As Long = 2     'index of the subclassed hWnd OR hook type
  38.     Private Const IDX_PREVPROC  As Long = 9     'Thunk data index of the original WndProc
  39.     Private Const IDX_BTABLE    As Long = 11    'Thunk data index of the Before table for messages
  40.     Private Const IDX_ATABLE    As Long = 12    'Thunk data index of the After table for messages
  41.     Private Const IDX_CALLBACKORDINAL As Long = 36 ' Ubound(callback thunkdata)+1, index of the callback
  42.  
  43.   ' Declarations:
  44.     Private Declare Sub RtlMoveMemory Lib "kernel32" (ByVal Destination As Long, ByVal Source As Long, ByVal Length As Long)
  45.     Private Declare Function IsBadCodePtr Lib "kernel32" (ByVal lpfn As Long) As Long
  46.     Private Declare Function VirtualAlloc Lib "kernel32" (ByVal lpAddress As Long, ByVal dwSize As Long, ByVal flAllocationType As Long, ByVal flProtect As Long) As Long
  47.     Private Declare Function VirtualFree Lib "kernel32" (ByVal lpAddress As Long, ByVal dwSize As Long, ByVal dwFreeType As Long) As Long
  48.     Private Declare Function GetModuleHandleA Lib "kernel32" (ByVal lpModuleName As String) As Long
  49.     Private Declare Function GetModuleHandleW Lib "kernel32" (ByVal lpModuleName As Long) As Long
  50.     Private Declare Function GetProcAddress Lib "kernel32" (ByVal hModule As Long, ByVal lpProcName As String) As Long
  51.     
  52.     Private Enum eThunkType
  53.         SubclassThunk = 0
  54.     End Enum
  55.  
  56.     '-Selfsub specific declarations----------------------------------------------------------------------------
  57.     Private Enum eMsgWhen                                                   'When to callback
  58.       MSG_BEFORE = 1                                                        'Callback before the original WndProc
  59.       MSG_AFTER = 2                                                         'Callback after the original WndProc
  60.       MSG_BEFORE_AFTER = MSG_BEFORE Or MSG_AFTER                            'Callback before and after the original WndProc
  61.     End Enum
  62.     
  63.     ' see ssc_Subclass for complete listing of indexes and what they relate to
  64.     Private Const IDX_PARM_USER As Long = 13    'Thunk data index of the User-defined callback parameter data index
  65.     Private Const IDX_UNICODE   As Long = 107   'Must be UBound(subclass thunkdata)+1; index for unicode support
  66.     Private Const MSG_ENTRIES   As Long = 32    'Number of msg table entries. Set to 1 if using ALL_MESSAGES for all subclassed windows
  67.     Private Const ALL_MESSAGES  As Long = -1    'All messages will callback
  68.     
  69.     Private Declare Function GetCurrentProcessId Lib "kernel32" () As Long
  70.     Private Declare Function GetWindowThreadProcessId Lib "user32" (ByVal hwnd As Long, lpdwProcessId As Long) As Long
  71.     Private Declare Function IsWindow Lib "user32" (ByVal hwnd As Long) As Long
  72.     Private Declare Function IsWindowUnicode Lib "user32.dll" (ByVal hwnd As Long) As Long
  73.     Private Declare Function SetWindowLongA Lib "user32" (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
  74.     Private Declare Function SetWindowLongW Lib "user32" (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
  75. '------------------------------------------------------------------------------------------------------------------------------------
  76.  
  77.  
  78. Private Const DEF_DEPTH             As Long = 7
  79. Private Const DEF_TRANSPARENCY      As Long = 140
  80.  
  81. Private Const AC_SRC_OVER           As Long = &H0
  82. Private Const AC_SRC_ALPHA          As Long = &H1
  83. Private Const BITSPIXEL             As Long = 12
  84. Private Const SIZE_RESTORED         As Long = 0
  85. Private Const SW_HIDE               As Long = 0
  86. Private Const SW_SHOWNOACTIVATE     As Long = 4
  87. Private Const SWP_HIDEWINDOW        As Long = &H80
  88. Private Const SWP_SHOWWINDOW        As Long = &H40
  89. Private Const ULW_ALPHA             As Long = &H2
  90.  
  91. Private Const WM_DISPLAYCHANGE      As Long = &H7E
  92. Private Const WM_THEMECHANGED       As Long = &H31A
  93. Private Const WM_WINDOWPOSCHANGED   As Long = &H47
  94. Private Const WM_SIZE               As Long = &H5
  95. Private Const WS_EX_LAYERED         As Long = &H80000
  96. Private Const WS_EX_NOPARENTNOTIFY  As Long = &H4
  97. Private Const WS_EX_TRANSPARENT     As Long = &H20
  98. Private Const WS_POPUP              As Long = &H80000000
  99.  
  100. Private Type tBGRA
  101.     Blue                            As Byte
  102.     Green                           As Byte
  103.     Red                             As Byte
  104.     Alpha                           As Byte
  105. End Type
  106.  
  107. Private Type tBITMAPINFOHEADER
  108.   biSize                            As Long
  109.   biWidth                           As Long
  110.   biHeight                          As Long
  111.   biPlanes                          As Integer
  112.   biBitCount                        As Integer
  113.   biCompression                     As Long
  114.   biSizeImage                       As Long
  115.   biXPelsPerMeter                   As Long
  116.   biYPelsPerMeter                   As Long
  117.   biClrUsed                         As Long
  118.   biClrImportant                    As Long
  119. End Type
  120.  
  121. Private Type tBLENDFUNCTION
  122.   BlendOp                           As Byte
  123.   BlendFlags                        As Byte
  124.   SourceConstantAlpha               As Byte
  125.   AlphaFormat                       As Byte
  126. End Type
  127.  
  128. Private Type tOSVERSIONINFO
  129.   dwOSVersionInfoSize               As Long
  130.   dwMajorVersion                    As Long
  131.   dwMinorVersion                    As Long
  132.   dwBuildNumber                     As Long
  133.   dwPlatformId                      As Long
  134.   szCSDVersion                      As String * 128
  135. End Type
  136.  
  137. Private Type tPOINT
  138.   X                                 As Long
  139.   Y                                 As Long
  140. End Type
  141.  
  142. Private Type tSAFEARRAYBOUND
  143.   cElements                         As Long
  144.   lLbound                           As Long
  145. End Type
  146.  
  147. Private Type tSAFEARRAY2D
  148.   cDims                             As Integer
  149.   fFeatures                         As Integer
  150.   cbElements                        As Long
  151.   cLocks                            As Long
  152.   pvData                            As Long
  153.   Bounds(0 To 1)                    As tSAFEARRAYBOUND
  154. End Type
  155.  
  156. Private Type tSIZE
  157.   cx                                As Long
  158.   cy                                As Long
  159. End Type
  160.  
  161. Private Type tWINDOWPOS
  162.   hwnd                              As Long
  163.   hWndInsertAfter                   As Long
  164.   X                                 As Long
  165.   Y                                 As Long
  166.   cx                                As Long
  167.   cy                                As Long
  168.   Flags                             As Long
  169. End Type
  170.  
  171. Private m_Color                     As Long                                 'Private shadow color property value
  172. Private m_Depth                     As Long                                 'Private shadow depth property value
  173. Private m_Transparency              As Long                                 'Private shadow transparency property value
  174.  
  175. Private bIsLayered                  As Boolean                              'Layered windows supported
  176. Private bIsLuna                     As Boolean                              'Luna theme?
  177. Private bIsXP                       As Boolean                              'Windows XP (or greater)?
  178. Private bLastShow                   As Boolean                              'The previous show state
  179. Private cx                          As Long                                 'Width
  180. Private cy                          As Long                                 'Height
  181. Private hWndBt                      As Long                                 'Bottom shadow window handle
  182. Private hWndRt                      As Long                                 'Right shadow window handle
  183. Private hWndForm                    As Long                                 'Parent window handle
  184. Private wp                          As tWINDOWPOS                           'Parent window position
  185.  
  186. Private Declare Function CreateDIBSection Lib "gdi32" (ByVal hdc As Long, pBitmapInfo As tBITMAPINFOHEADER, ByVal un As Long, ByRef lplpVoid As Long, ByVal handle As Long, ByVal dw As Long) As Long
  187. Private Declare Function CreateCompatibleDC Lib "gdi32" (ByVal hdc As Long) As Long
  188. Private Declare Function CreateWindowExA Lib "user32" (ByVal dwExStyle As Long, ByVal lpClassName As String, ByVal lpWindowName As String, ByVal dwStyle As Long, ByVal X As Long, ByVal Y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal hWndParent As Long, ByVal hMenu As Long, ByVal hInstance As Long, lpParam As Any) As Long
  189. Private Declare Function DeleteDC Lib "gdi32" (ByVal hdc As Long) As Long
  190. Private Declare Function DestroyWindow Lib "user32" (ByVal hwnd As Long) As Long
  191. Private Declare Function FreeLibrary Lib "kernel32" (ByVal hLibModule As Long) As Long
  192. Private Declare Function GetCurrentThemeName Lib "uxtheme.dll" (ByVal pszThemeFileName As Long, ByVal cchMaxNameChars As Long, ByVal pszColorBuff As Long, ByVal cchMaxColorChars As Long, ByVal pszSizeBuff As Long, ByVal cchMaxSizeChars As Long) As Long
  193. Private Declare Function GetDC Lib "user32" (ByVal hwnd As Long) As Long
  194. Private Declare Function GetDeviceCaps Lib "gdi32" (ByVal hdc As Long, ByVal nIndex As Long) As Long
  195. Private Declare Function GetSysColor Lib "user32" (ByVal nIndex As Long) As Long
  196. Private Declare Function GetThemeDocumentationProperty Lib "uxtheme.dll" (ByVal pszThemeName As Long, ByVal pszPropertyName As Long, ByVal pszValueBuff As Long, ByVal cchMaxValChars As Long) As Long
  197. Private Declare Function GetVersionExA Lib "kernel32" (lpVersionInformation As tOSVERSIONINFO) As Long
  198. Private Declare Function IsWindowVisible Lib "user32" (ByVal hwnd As Long) As Long
  199. Private Declare Function LoadLibraryA Lib "kernel32" (ByVal lpLibFileName As String) As Long
  200. Private Declare Function MoveWindow Lib "user32" (ByVal hwnd As Long, ByVal X As Long, ByVal Y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal bRepaint As Long) As Long
  201. Private Declare Function SelectObject Lib "gdi32" (ByVal hdc As Long, ByVal hObject As Long) As Long
  202. Private Declare Function ShowWindow Lib "user32" (ByVal hwnd As Long, ByVal nCmdShow As Long) As Long
  203. Private Declare Function UpdateLayeredWindow Lib "user32.dll" (ByVal hwnd As Long, ByVal hdcDest As Long, ptDst As Any, pSize As Any, ByVal hDCSrc As Long, ptSrc As Any, ByVal crKey As Long, pBlend As Any, ByVal dwFlags As Long) As Long
  204. Private Declare Function VarPtrArray Lib "msvbvm60.dll" Alias "VarPtr" (Ptr() As Any) As Long
  205. Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
  206.  
  207. Private Sub Class_Initialize()                                              'Class initialization
  208.   CheckEnvironment
  209.   m_Depth = DEF_DEPTH
  210.   m_Transparency = DEF_TRANSPARENCY
  211. End Sub
  212.  
  213. Private Sub Class_Terminate()
  214.   ssc_Terminate                                                              'Terminate all subclassing
  215.   
  216.   If hWndRt <> 0 Then
  217.     DestroyWindow hWndRt
  218.     DestroyWindow hWndBt
  219.   End If
  220. End Sub
  221.  
  222. 'Shadow depth property
  223. Public Property Get Color() As Long
  224.   Color = m_Color
  225. End Property
  226. '
  227. Public Property Let Color(ByVal newValue As Long)
  228.   Const COLOR_SYS_MASK As Long = &H80000000
  229.   
  230.   If (newValue And COLOR_SYS_MASK) Then                                       'If the system color bit is set
  231.     newValue = GetSysColor(newValue Xor COLOR_SYS_MASK)                       'Get the translated system color
  232.   End If
  233.   
  234.   If newValue <> m_Color Then
  235.     m_Color = newValue
  236.     DisplayShadows
  237.   End If
  238. End Property
  239.  
  240. 'Shadow depth property
  241. Public Property Get Depth() As Long
  242.   Depth = m_Depth
  243. End Property
  244. '
  245. Public Property Let Depth(ByVal newValue As Long)
  246.   If newValue <> m_Depth Then
  247.     m_Depth = newValue
  248.     DisplayShadows
  249.   End If
  250. End Property
  251.  
  252. 'Shadow transparency property
  253. Public Property Get Transparency() As Byte
  254.   Transparency = CByte(m_Transparency)
  255. End Property
  256. '
  257. Public Property Let Transparency(ByVal newValue As Byte)
  258.   If newValue <> m_Transparency Then
  259.     m_Transparency = CLng(newValue)
  260.     DisplayShadows
  261.   End If
  262. End Property
  263.  
  264. 'Set the form to be shadowed
  265. Public Function Shadow(frm As Form) As Boolean
  266.   If hWndForm <> 0 Then
  267.     zError "Shadow", "Only a single form per cShadow instance is allowed"
  268.     Exit Function
  269.   End If
  270.  
  271.   If bIsLayered Then
  272.     hWndForm = frm.hwnd
  273.   
  274.     If ssc_Subclass(hWndForm) Then
  275.       ssc_AddMsg hWndForm, MSG_BEFORE, WM_WINDOWPOSCHANGED
  276.       ssc_AddMsg hWndForm, MSG_BEFORE, WM_SIZE
  277.       ssc_AddMsg hWndForm, MSG_BEFORE, WM_DISPLAYCHANGE
  278.       
  279.       If bIsXP Then
  280.         ssc_AddMsg hWndForm, MSG_BEFORE, WM_THEMECHANGED
  281.       End If
  282.       
  283.       CreateWindows
  284.       Shadow = True
  285.     End If
  286.   End If
  287. End Function
  288.  
  289. 'Validate the OS and color depth
  290. Private Sub CheckEnvironment()
  291.   Dim OSV As tOSVERSIONINFO
  292.  
  293.   bIsLayered = False
  294.  
  295.   With OSV
  296.     .dwOSVersionInfoSize = Len(OSV)                                         'Set the length element
  297.     GetVersionExA OSV                                                       'Fill the type with OS version info
  298.  
  299.     If .dwPlatformId = 2 Then                                               'If it's an NT based OS
  300.       If .dwMajorVersion >= 5 Then                                           'If the major version is 5 or greater, OS supports transparency
  301.         If .dwMinorVersion > 0 Then
  302.           bIsXP = True
  303.           bIsLuna = IsLuna                                                  'Determine if the Luna theme is active
  304.         End If
  305.  
  306.         bIsLayered = (GetDeviceCaps(GetDC(0), BITSPIXEL) >= 16)             'Ensure we have enough screen colors
  307.       End If
  308.     End If
  309.   End With
  310. End Sub
  311.  
  312. 'Create the shadow windows
  313. Private Sub CreateWindows()
  314.   Const EX_STYLE As Long = WS_EX_LAYERED Or WS_EX_TRANSPARENT Or WS_EX_NOPARENTNOTIFY
  315.  
  316.   hWndRt = CreateWindowExA(EX_STYLE, "#32770", vbNullString, WS_POPUP, 0, 0, 0, 0, hWndForm, 0, App.hInstance, 0)
  317.   hWndBt = CreateWindowExA(EX_STYLE, "#32770", vbNullString, WS_POPUP, 0, 0, 0, 0, hWndForm, 0, App.hInstance, 0)
  318. End Sub
  319.  
  320. 'Display the right and bottom shadows
  321. Private Sub DisplayShadows()
  322.   If bIsLayered Then
  323.     If IsWindowVisible(hWndForm) <> 0 Then
  324.       With wp
  325.         DisplayShadowSub .X + .cx, .Y + m_Depth, m_Depth, .cy, True
  326.         DisplayShadowSub .X + m_Depth, .Y + .cy, .cx - m_Depth, m_Depth, False
  327.       End With
  328.     End If
  329.   End If
  330. End Sub
  331.  
  332. 'Display the content of the specified shadow window
  333. Private Sub DisplayShadowSub(ByVal X As Long, ByVal Y As Long, cx As Long, cy As Long, ByVal bRight As Boolean)
  334.   Dim DC        As Long
  335.   Dim iX        As Long
  336.   Dim iY        As Long
  337.   Dim hDib      As Long
  338.   Dim hWin      As Long
  339.   Dim nAlpha    As Long
  340.   Dim aPixels() As Long
  341.   Dim pBmpBits  As Long
  342.   Dim pt0       As tPOINT
  343.   Dim pt        As tPOINT
  344.   Dim sz        As tSIZE
  345.   Dim bs        As tBLENDFUNCTION
  346.   Dim bmpHeader As tBITMAPINFOHEADER
  347.   Dim SafeArray As tSAFEARRAY2D
  348.  
  349.   DC = CreateCompatibleDC(0)                                                'Get a screen compatible memory dc
  350.  
  351.   With bmpHeader                                                            'Initialize a bitmap header
  352.     .biSize = Len(bmpHeader)                                                'Bitmap header size
  353.     .biWidth = cx                                                           'Bitmap/window pixel width
  354.     .biHeight = cy                                                          'Bitmap/window pixel height
  355.     .biPlanes = 1                                                           'Graphics planes
  356.     .biBitCount = 32                                                        '32bits per pixel BGRA (Blue, Green, Red, Alpha)
  357.     .biSizeImage = cx * cy * 4                                              'Memory size, width * height * 32bit
  358.   End With
  359.  
  360.   hDib = CreateDIBSection(DC, bmpHeader, 0, pBmpBits, 0, 0)                 'Create a device independant bitmap as per the header, compatible with the dc (compatible with the screen)
  361.  
  362.   With SafeArray                                                            'Construct a VB safearray header that matches the specs of the bitmap
  363.     .cbElements = 4                                                         '4 bytes per element - 32bits per pixel
  364.     .cDims = 2                                                              'We'll treat the pixels as a two dimensional (x, y) array
  365.     .pvData = pBmpBits                                                      'The data pointer points to the bitmap data (pixels)
  366.     .Bounds(0).lLbound = 0                                                  'Lowest bound will be 0
  367.     .Bounds(0).cElements = cy                                               'The number of elements
  368.     .Bounds(1).lLbound = 0                                                  'Lowest bound will be 0
  369.     .Bounds(1).cElements = cx                                               'The number of elements
  370.   End With
  371.  
  372.   CopyMemory ByVal VarPtrArray(aPixels()), VarPtr(SafeArray), 4             'Copy the address of our safearray over the address of aPixels() safearray
  373.  
  374.   If bRight Then
  375.     hWin = hWndRt
  376.   Else
  377.     hWin = hWndBt
  378.   End If
  379.  
  380.   If bRight Then
  381.     For iY = 0 To cy - 1
  382.       If iY < cx Then
  383.         nAlpha = (255 * iY) \ cx
  384.       ElseIf iY >= (cy - cx) Then
  385.         nAlpha = ((cy - iY) * 255) \ cx
  386.       Else
  387.         nAlpha = 255
  388.       End If
  389.  
  390.       For iX = 0 To cx - 1
  391.         aPixels(iX, iY) = MakeBGRA((nAlpha * (cx - iX)) \ cx)
  392.       Next iX
  393.     Next iY
  394.   Else
  395.     For iX = 0 To cx - 1
  396.       If iX < cy Then
  397.         nAlpha = (255 * iX) \ cy
  398.       Else
  399.         nAlpha = 255
  400.       End If
  401.  
  402.       For iY = 0 To cy - 1
  403.         aPixels(iX, iY) = MakeBGRA((nAlpha * iY) \ cy)
  404.       Next iY
  405.     Next iX
  406.   End If
  407.  
  408.   If bRight Then
  409.     If bIsLuna Then
  410.       On Error Resume Next                                                  'Protect against shadow depths less than 5
  411.       aPixels(cx - 1, cy - 1) = 0
  412.       aPixels(cx - 2, cy - 1) = 0
  413.       aPixels(cx - 3, cy - 1) = 0
  414.       aPixels(cx - 4, cy - 1) = 0
  415.       aPixels(cx - 5, cy - 1) = 0
  416.  
  417.       aPixels(cx - 1, cy - 2) = 0
  418.       aPixels(cx - 2, cy - 2) = 0
  419.       aPixels(cx - 3, cy - 2) = 0
  420.  
  421.       aPixels(cx - 1, cy - 3) = 0
  422.       aPixels(cx - 2, cy - 3) = 0
  423.  
  424.       aPixels(cx - 1, cy - 4) = 0
  425.       aPixels(cx - 1, cy - 5) = 0
  426.       On Error GoTo 0
  427.     End If
  428.   End If
  429.  
  430.   CopyMemory ByVal VarPtrArray(aPixels()), 0&, 4
  431.   
  432.   With bs                                                                   'Setup the blend function
  433.     .AlphaFormat = AC_SRC_ALPHA                                             'Use the alpha channel for individual pixel transparency
  434.     .BlendFlags = 0
  435.     .BlendOp = AC_SRC_OVER                                                  'Alpha overlay
  436.     .SourceConstantAlpha = m_Transparency                                   'Alpha transparency for overall transparency
  437.   End With
  438.  
  439.   pt.X = X                                                                  'Setup the window position and size data
  440.   pt.Y = Y
  441.   sz.cx = cx
  442.   sz.cy = cy
  443.  
  444.   hDib = SelectObject(DC, hDib)                                             'Select the bitmap into the memory display context
  445.   UpdateLayeredWindow hWin, DC, pt, sz, DC, pt0, 0, bs, ULW_ALPHA           'Do the layered update
  446.   SelectObject DC, hDib                                                     'Trash the bitmap
  447.   DeleteDC DC                                                               'Delete the memory display context
  448. End Sub
  449.  
  450. 'Return whether the Luna theme is active
  451. Private Function IsLuna() As Boolean
  452.   Dim hLib   As Long
  453.   Dim nPos   As Long
  454.   Dim sTheme As String
  455.   Dim sName  As String
  456.  
  457.   hLib = LoadLibraryA("uxtheme.dll")
  458.  
  459.   If hLib <> 0 Then
  460.     sTheme = String$(255, 0)
  461.  
  462.     GetCurrentThemeName StrPtr(sTheme), Len(sTheme), 0, 0, 0, 0
  463.     nPos = InStr(1, sTheme, vbNullChar)
  464.  
  465.     If nPos > 0 Then
  466.       sTheme = Left$(sTheme, nPos - 1)
  467.       sName = String$(255, 0)
  468.  
  469.       GetThemeDocumentationProperty StrPtr(sTheme), StrPtr("ThemeName"), StrPtr(sName), Len(sName)
  470.       nPos = InStr(1, sName, vbNullChar)
  471.  
  472.       If nPos > 0 Then
  473.         sName = Left$(sName, nPos - 1)
  474.         bIsLuna = (StrComp(sName, "Luna", vbTextCompare) = 0)
  475.       End If
  476.     End If
  477.  
  478.     FreeLibrary hLib
  479.   End If
  480. End Function
  481.  
  482. 'Pre-multiply the shadow color with the passed alpha value. This is needed to get nice looking colors according to MSDN.
  483. Private Function MakeBGRA(ByVal Alpha As Byte) As Long
  484.   Dim fFactor As Double
  485.   Dim BGRA    As tBGRA
  486.     
  487.   fFactor = CDbl(Alpha) / 255#                                              'Calculate the factor
  488.   
  489. 'Note that nColor is in RGB format, part of this process is to convert to BGRA format
  490.   With BGRA                                                                 'Blue, Green, Red, Alpha
  491.     .Blue = ((m_Color And &HFF0000) \ &H10000) * fFactor                     'Factor the blue component
  492.     .Green = ((m_Color And &HFF00&) \ &H100&) * fFactor                      'Factor the green component
  493.     .Red = (m_Color And &HFF) * fFactor                                      'Factor the red component
  494.     .Alpha = Alpha                                                          'Store the alpha value
  495.   End With
  496.   
  497. 'Copy the BGRA type to long
  498.   CopyMemory MakeBGRA, BGRA, 4
  499. End Function
  500.  
  501. 'Show/hide the shadow windows
  502. Private Sub Show(ByVal bShow As Boolean, Optional ByVal bForce As Boolean = False)
  503.   If Not bForce Then
  504.     If bLastShow = bShow Then
  505.       Exit Sub
  506.     End If
  507.   End If
  508.  
  509.   bLastShow = bShow
  510.  
  511.   If bShow Then
  512.     ShowWindow hWndRt, SW_SHOWNOACTIVATE
  513.     ShowWindow hWndBt, SW_SHOWNOACTIVATE
  514.   Else
  515.     ShowWindow hWndRt, SW_HIDE
  516.     ShowWindow hWndBt, SW_HIDE
  517.   End If
  518. End Sub
  519.  
  520. 'Size/position the shadows
  521. Private Sub SizePos()
  522.   With wp
  523.     If .Flags And SWP_HIDEWINDOW Then                                       'If the parent form is being hidden
  524.       Show False                                                            'Hide the shadow windows also
  525.  
  526.     Else
  527.       If .cx <> cx Then                                                     'If the parent's width has changed
  528.         cx = .cx                                                            'Store the new width
  529.         DisplayShadowSub .X + m_Depth, .Y + .cy, .cx - m_Depth, m_Depth, False
  530.       End If
  531.  
  532.       If .cy <> cy Then                                                     'If the parent's height has changed
  533.         cy = .cy                                                            'Store the new height
  534.         DisplayShadowSub .X + .cx, .Y + m_Depth, m_Depth, .cy, True
  535.       End If
  536.  
  537.       MoveWindow hWndRt, .X + .cx, .Y + m_Depth, m_Depth, .cy, False
  538.       MoveWindow hWndBt, .X + m_Depth, .Y + .cy, .cx - m_Depth, m_Depth, False
  539.  
  540.       If (.Flags And SWP_SHOWWINDOW) Then
  541.         Show True
  542.       End If
  543.     End If
  544.   End With
  545. End Sub
  546.  
  547.  
  548. '-SelfSub code------------------------------------------------------------------------------------
  549. '-The following routines are exclusively for the ssc_Subclass routines----------------------------
  550. Private Function ssc_Subclass(ByVal lng_hWnd As Long, _
  551.                     Optional ByVal lParamUser As Long = 0, _
  552.                     Optional ByVal nOrdinal As Long = 1, _
  553.                     Optional ByVal oCallback As Object = Nothing, _
  554.                     Optional ByVal bIdeSafety As Boolean = True, _
  555.                     Optional ByRef bUnicode As Boolean = False, _
  556.                     Optional ByVal bIsAPIwindow As Boolean = False) As Boolean 'Subclass the specified window handle
  557.  
  558.     '*************************************************************************************************
  559.     '* lng_hWnd   - Handle of the window to subclass
  560.     '* lParamUser - Optional, user-defined callback parameter
  561.     '* nOrdinal   - Optional, ordinal index of the callback procedure. 1 = last private method, 2 = second last private method, etc.
  562.     '* oCallback  - Optional, the object that will receive the callback. If undefined, callbacks are sent to this object's instance
  563.     '* bIdeSafety - Optional, enable/disable IDE safety measures. There is not reason to set this to False
  564.     '* bUnicode - Optional, if True, Unicode API calls should be made to the window vs ANSI calls
  565.     '*            Parameter is byRef and its return value should be checked to know if ANSI to be used or not
  566.     '* bIsAPIwindow - Optional, if True DestroyWindow will be called if IDE ENDs
  567.     '*****************************************************************************************
  568.     '** Subclass.asm - subclassing thunk
  569.     '**
  570.     '** Paul_Caton@hotmail.com
  571.     '** Copyright free, use and abuse as you see fit.
  572.     '**
  573.     '** v2.0 Re-write by LaVolpe, based mostly on Paul Caton's original thunks....... 20070720
  574.     '** .... Reorganized & provided following additional logic
  575.     '** ....... Unsubclassing only occurs after thunk is no longer recursed
  576.     '** ....... Flag used to bypass callbacks until unsubclassing can occur
  577.     '** ....... Timer used as delay mechanism to free thunk memory afer unsubclassing occurs
  578.     '** .............. Prevents crash when one window subclassed multiple times
  579.     '** .............. More END safe, even if END occurs within the subclass procedure
  580.     '** ....... Added ability to destroy API windows when IDE terminates
  581.     '** ....... Added auto-unsubclass when WM_NCDESTROY received
  582.     '*****************************************************************************************
  583.     ' Subclassing procedure must be declared identical to the one at the end of this class (Sample at Ordinal #1)
  584.  
  585.     Dim z_Sc(0 To IDX_UNICODE) As Long                 'Thunk machine-code initialised here
  586.     
  587.     Const SUB_NAME      As String = "ssc_Subclass"     'This routine's name
  588.     Const CODE_LEN      As Long = 4 * IDX_UNICODE + 4  'Thunk length in bytes
  589.     Const PAGE_RWX      As Long = &H40&                'Allocate executable memory
  590.     Const MEM_COMMIT    As Long = &H1000&              'Commit allocated memory
  591.     Const MEM_RELEASE   As Long = &H8000&              'Release allocated memory flag
  592.     Const GWL_WNDPROC   As Long = -4                   'SetWindowsLong WndProc index
  593.     Const WNDPROC_OFF   As Long = &H60                 'Thunk offset to the WndProc execution address
  594.     Const MEM_LEN       As Long = CODE_LEN + (8 * (MSG_ENTRIES + 1)) 'Bytes to allocate per thunk, data + code + msg tables
  595.     
  596.   ' This is the complete listing of thunk offset values and what they point/relate to.
  597.   ' Those rem'd out are used elsewhere or are initialized in Declarations section
  598.   
  599.   'Const IDX_RECURSION  As Long = 0     'Thunk data index of callback recursion count
  600.   'Const IDX_SHUTDOWN   As Long = 1     'Thunk data index of the termination flag
  601.   'Const IDX_INDEX      As Long = 2     'Thunk data index of the subclassed hWnd
  602.    Const IDX_EBMODE     As Long = 3     'Thunk data index of the EbMode function address
  603.    Const IDX_CWP        As Long = 4     'Thunk data index of the CallWindowProc function address
  604.    Const IDX_SWL        As Long = 5     'Thunk data index of the SetWindowsLong function address
  605.    Const IDX_FREE       As Long = 6     'Thunk data index of the VirtualFree function address
  606.    Const IDX_BADPTR     As Long = 7     'Thunk data index of the IsBadCodePtr function address
  607.    Const IDX_OWNER      As Long = 8     'Thunk data index of the Owner object's vTable address
  608.   'Const IDX_PREVPROC   As Long = 9     'Thunk data index of the original WndProc
  609.    Const IDX_CALLBACK   As Long = 10    'Thunk data index of the callback method address
  610.   'Const IDX_BTABLE     As Long = 11    'Thunk data index of the Before table
  611.   'Const IDX_ATABLE     As Long = 12    'Thunk data index of the After table
  612.   'Const IDX_PARM_USER  As Long = 13    'Thunk data index of the User-defined callback parameter data index
  613.    Const IDX_DW         As Long = 14    'Thunk data index of the DestroyWinodw function address
  614.    Const IDX_ST         As Long = 15    'Thunk data index of the SetTimer function address
  615.    Const IDX_KT         As Long = 16    'Thunk data index of the KillTimer function address
  616.    Const IDX_EBX_TMR    As Long = 20    'Thunk code patch index of the thunk data for the delay timer
  617.    Const IDX_EBX        As Long = 26    'Thunk code patch index of the thunk data
  618.   'Const IDX_UNICODE    As Long = xx    'Must be UBound(subclass thunkdata)+1; index for unicode support
  619.     
  620.     Dim z_ScMem       As Long           'Thunk base address
  621.     Dim nAddr         As Long
  622.     Dim nID           As Long
  623.     Dim nMyID         As Long
  624.     Dim bIDE          As Boolean
  625.  
  626.     If IsWindow(lng_hWnd) = 0 Then      'Ensure the window handle is valid
  627.         zError SUB_NAME, "Invalid window handle"
  628.         Exit Function
  629.     End If
  630.     
  631.     nMyID = GetCurrentProcessId                         'Get this process's ID
  632.     GetWindowThreadProcessId lng_hWnd, nID              'Get the process ID associated with the window handle
  633.     If nID <> nMyID Then                                'Ensure that the window handle doesn't belong to another process
  634.         zError SUB_NAME, "Window handle belongs to another process"
  635.         Exit Function
  636.     End If
  637.     
  638.     If oCallback Is Nothing Then Set oCallback = Me     'If the user hasn't specified the callback owner
  639.     
  640.     nAddr = zAddressOf(oCallback, nOrdinal)             'Get the address of the specified ordinal method
  641.     If nAddr = 0 Then                                   'Ensure that we've found the ordinal method
  642.         zError SUB_NAME, "Callback method not found"
  643.         Exit Function
  644.     End If
  645.         
  646.     z_ScMem = VirtualAlloc(0, MEM_LEN, MEM_COMMIT, PAGE_RWX) 'Allocate executable memory
  647.     
  648.     If z_ScMem <> 0 Then                                  'Ensure the allocation succeeded
  649.     
  650.       If z_scFunk Is Nothing Then Set z_scFunk = New Collection 'If this is the first time through, do the one-time initialization
  651.       On Error GoTo CatchDoubleSub                              'Catch double subclassing
  652.         z_scFunk.Add z_ScMem, "h" & lng_hWnd                    'Add the hWnd/thunk-address to the collection
  653.       On Error GoTo 0
  654.       
  655.    'z_Sc (0) thru z_Sc(17) are used as storage for the thunks & IDX_ constants above relate to these thunk positions which are filled in below
  656.     z_Sc(18) = &HD231C031: z_Sc(19) = &HBBE58960: z_Sc(21) = &H21E8F631: z_Sc(22) = &HE9000001: z_Sc(23) = &H12C&: z_Sc(24) = &HD231C031: z_Sc(25) = &HBBE58960: z_Sc(27) = &H3FFF631: z_Sc(28) = &H75047339: z_Sc(29) = &H2873FF23: z_Sc(30) = &H751C53FF: z_Sc(31) = &HC433913: z_Sc(32) = &H53FF2274: z_Sc(33) = &H13D0C: z_Sc(34) = &H18740000: z_Sc(35) = &H875C085: z_Sc(36) = &H820443C7: z_Sc(37) = &H90000000: z_Sc(38) = &H87E8&: z_Sc(39) = &H22E900: z_Sc(40) = &H90900000: z_Sc(41) = &H2C7B8B4A: z_Sc(42) = &HE81C7589: z_Sc(43) = &H90&: z_Sc(44) = &H75147539: z_Sc(45) = &H6AE80F: z_Sc(46) = &HD2310000: z_Sc(47) = &HE8307B8B: z_Sc(48) = &H7C&: z_Sc(49) = &H7D810BFF: z_Sc(50) = &H8228&: z_Sc(51) = &HC7097500: z_Sc(52) = &H80000443: z_Sc(53) = &H90900000: z_Sc(54) = &H44753339: z_Sc(55) = &H74047339: z_Sc(56) = &H2473FF3F: z_Sc(57) = &HFFFFFC68
  657.     z_Sc(58) = &H2475FFFF: z_Sc(59) = &H811453FF: z_Sc(60) = &H82047B: z_Sc(61) = &HC750000: z_Sc(62) = &H74387339: z_Sc(63) = &H2475FF07: z_Sc(64) = &H903853FF: z_Sc(65) = &H81445B89: z_Sc(66) = &H484443: z_Sc(67) = &H73FF0000: z_Sc(68) = &H646844: z_Sc(69) = &H56560000: z_Sc(70) = &H893C53FF: z_Sc(71) = &H90904443: z_Sc(72) = &H10C261: z_Sc(73) = &H53E8&: z_Sc(74) = &H3075FF00: z_Sc(75) = &HFF2C75FF: z_Sc(76) = &H75FF2875: z_Sc(77) = &H2473FF24: z_Sc(78) = &H891053FF: z_Sc(79) = &H90C31C45: z_Sc(80) = &H34E30F8B: z_Sc(81) = &H1078C985: z_Sc(82) = &H4C781: z_Sc(83) = &H458B0000: z_Sc(84) = &H75AFF228: z_Sc(85) = &H90909023: z_Sc(86) = &H8D144D8D: z_Sc(87) = &H8D503443: z_Sc(88) = &H75FF1C45: z_Sc(89) = &H2C75FF30: z_Sc(90) = &HFF2875FF: z_Sc(91) = &H51502475: z_Sc(92) = &H2073FF52: z_Sc(93) = &H902853FF: z_Sc(94) = &H909090C3: z_Sc(95) = &H74447339: z_Sc(96) = &H4473FFF7
  658.     z_Sc(97) = &H4053FF56: z_Sc(98) = &HC3447389: z_Sc(99) = &H89285D89: z_Sc(100) = &H45C72C75: z_Sc(101) = &H800030: z_Sc(102) = &H20458B00: z_Sc(103) = &H89145D89: z_Sc(104) = &H81612445: z_Sc(105) = &H4C4&: z_Sc(106) = &H1862FF00
  659.  
  660.     ' cache callback related pointers & offsets
  661.       z_Sc(IDX_EBX) = z_ScMem                                                 'Patch the thunk data address
  662.       z_Sc(IDX_EBX_TMR) = z_ScMem                                             'Patch the thunk data address
  663.       z_Sc(IDX_INDEX) = lng_hWnd                                              'Store the window handle in the thunk data
  664.       z_Sc(IDX_BTABLE) = z_ScMem + CODE_LEN                                   'Store the address of the before table in the thunk data
  665.       z_Sc(IDX_ATABLE) = z_ScMem + CODE_LEN + ((MSG_ENTRIES + 1) * 4)         'Store the address of the after table in the thunk data
  666.       z_Sc(IDX_OWNER) = ObjPtr(oCallback)                                     'Store the callback owner's object address in the thunk data
  667.       z_Sc(IDX_CALLBACK) = nAddr                                              'Store the callback address in the thunk data
  668.       z_Sc(IDX_PARM_USER) = lParamUser                                        'Store the lParamUser callback parameter in the thunk data
  669.       
  670.       ' validate unicode request & cache unicode usage
  671.       If bUnicode Then bUnicode = (IsWindowUnicode(lng_hWnd) <> 0&)
  672.       z_Sc(IDX_UNICODE) = bUnicode                                            'Store whether the window is using unicode calls or not
  673.       
  674.       ' get function pointers for the thunk
  675.       If bIdeSafety = True Then                                               'If the user wants IDE protection
  676.           Debug.Assert zInIDE(bIDE)
  677.           If bIDE = True Then z_Sc(IDX_EBMODE) = zFnAddr("vba6", "EbMode", bUnicode) 'Store the EbMode function address in the thunk data
  678.                                                         '^^ vb5 users, change vba6 to vba5
  679.       End If
  680.       If bIsAPIwindow Then                                                    'If user wants DestroyWindow sent should IDE end
  681.           z_Sc(IDX_DW) = zFnAddr("user32", "DestroyWindow", bUnicode)
  682.       End If
  683.       z_Sc(IDX_FREE) = zFnAddr("kernel32", "VirtualFree", bUnicode)           'Store the VirtualFree function address in the thunk data
  684.       z_Sc(IDX_BADPTR) = zFnAddr("kernel32", "IsBadCodePtr", bUnicode)        'Store the IsBadCodePtr function address in the thunk data
  685.       z_Sc(IDX_ST) = zFnAddr("user32", "SetTimer", bUnicode)                  'Store the SetTimer function address in the thunk data
  686.       z_Sc(IDX_KT) = zFnAddr("user32", "KillTimer", bUnicode)                 'Store the KillTimer function address in the thunk data
  687.       
  688.       If bUnicode Then
  689.           z_Sc(IDX_CWP) = zFnAddr("user32", "CallWindowProcW", bUnicode)      'Store CallWindowProc function address in the thunk data
  690.           z_Sc(IDX_SWL) = zFnAddr("user32", "SetWindowLongW", bUnicode)       'Store the SetWindowLong function address in the thunk data
  691.           RtlMoveMemory z_ScMem, VarPtr(z_Sc(0)), CODE_LEN                    'Copy the thunk code/data to the allocated memory
  692.           z_Sc(IDX_PREVPROC) = SetWindowLongW(lng_hWnd, GWL_WNDPROC, z_ScMem + WNDPROC_OFF) 'Set the new WndProc, return the address of the original WndProc
  693.       Else
  694.           z_Sc(IDX_CWP) = zFnAddr("user32", "CallWindowProcA", bUnicode)      'Store CallWindowProc function address in the thunk data
  695.           z_Sc(IDX_SWL) = zFnAddr("user32", "SetWindowLongA", bUnicode)       'Store the SetWindowLong function address in the thunk data
  696.           RtlMoveMemory z_ScMem, VarPtr(z_Sc(0)), CODE_LEN                    'Copy the thunk code/data to the allocated memory
  697.           z_Sc(IDX_PREVPROC) = SetWindowLongA(lng_hWnd, GWL_WNDPROC, z_ScMem + WNDPROC_OFF) 'Set the new WndProc, return the address of the original WndProc
  698.       End If
  699.       If z_Sc(IDX_PREVPROC) = 0 Then                                          'Ensure the new WndProc was set correctly
  700.           zError SUB_NAME, "SetWindowLong failed, error #" & Err.LastDllError
  701.           GoTo ReleaseMemory
  702.       End If
  703.       'Store the original WndProc address in the thunk data
  704.       RtlMoveMemory z_ScMem + IDX_PREVPROC * 4, VarPtr(z_Sc(IDX_PREVPROC)), 4&
  705.       ssc_Subclass = True                                                     'Indicate success
  706.       
  707.     Else
  708.         zError SUB_NAME, "VirtualAlloc failed, error: " & Err.LastDllError
  709.         
  710.     End If
  711.  
  712.  Exit Function                                                                'Exit ssc_Subclass
  713.     
  714. CatchDoubleSub:
  715.  zError SUB_NAME, "Window handle is already subclassed"
  716.       
  717. ReleaseMemory:
  718.       VirtualFree z_ScMem, 0, MEM_RELEASE                                     'ssc_Subclass has failed after memory allocation, so release the memory
  719.       
  720. End Function
  721.  
  722. 'Terminate all subclassing
  723. Private Sub ssc_Terminate()
  724.     ' can be made public, can be removed & zTerminateThunks can be called instead
  725.     zTerminateThunks SubclassThunk
  726. End Sub
  727.  
  728. ''UnSubclass the specified window handle
  729. 'Private Sub ssc_UnSubclass(ByVal lng_hWnd As Long)
  730. '    ' can be made public, can be removed & zUnthunk can be called instead
  731. '    zUnThunk lng_hWnd, SubclassThunk
  732. 'End Sub
  733.  
  734. 'Add the message value to the window handle's specified callback table
  735. Private Sub ssc_AddMsg(ByVal lng_hWnd As Long, ByVal When As eMsgWhen, ParamArray Messages() As Variant)
  736.     
  737.     Dim z_ScMem       As Long                                   'Thunk base address
  738.     
  739.     z_ScMem = zMap_VFunction(lng_hWnd, SubclassThunk)           'Ensure that the thunk hasn't already released its memory
  740.     If z_ScMem Then
  741.       Dim M As Long
  742.       For M = LBound(Messages) To UBound(Messages)
  743.         Select Case VarType(Messages(M))                        ' ensure no strings, arrays, doubles, objects, etc are passed
  744.         Case vbByte, vbInteger, vbLong
  745.             If When And MSG_BEFORE Then                         'If the message is to be added to the before original WndProc table...
  746.               If zAddMsg(Messages(M), IDX_BTABLE, z_ScMem) = False Then 'Add the message to the before table
  747.                 When = (When And Not MSG_BEFORE)
  748.               End If
  749.             End If
  750.             If When And MSG_AFTER Then                          'If message is to be added to the after original WndProc table...
  751.               If zAddMsg(Messages(M), IDX_ATABLE, z_ScMem) = False Then 'Add the message to the after table
  752.                 When = (When And Not MSG_AFTER)
  753.               End If
  754.             End If
  755.         End Select
  756.       Next
  757.     End If
  758. End Sub
  759.  
  760. 'Add the message to the specified table of the window handle
  761. Private Function zAddMsg(ByVal uMsg As Long, ByVal nTable As Long, ByVal z_ScMem As Long) As Boolean
  762.       Dim nCount As Long                                                        'Table entry count
  763.       Dim nBase  As Long
  764.       Dim i      As Long                                                        'Loop index
  765.     
  766.       zAddMsg = True
  767.       nBase = zData(nTable, z_ScMem)                                            'Map zData() to the specified table
  768.       
  769.       If uMsg = ALL_MESSAGES Then                                               'If ALL_MESSAGES are being added to the table...
  770.         nCount = ALL_MESSAGES                                                   'Set the table entry count to ALL_MESSAGES
  771.       Else
  772.         
  773.         nCount = zData(0, nBase)                                                'Get the current table entry count
  774.         For i = 1 To nCount                                                     'Loop through the table entries
  775.           If zData(i, nBase) = 0 Then                                           'If the element is free...
  776.             zData(i, nBase) = uMsg                                              'Use this element
  777.             GoTo Bail                                                           'Bail
  778.           ElseIf zData(i, nBase) = uMsg Then                                    'If the message is already in the table...
  779.             GoTo Bail                                                           'Bail
  780.           End If
  781.         Next i                                                                  'Next message table entry
  782.     
  783.         nCount = i                                                             'On drop through: i = nCount + 1, the new table entry count
  784.         If nCount > MSG_ENTRIES Then                                           'Check for message table overflow
  785.           zError "zAddMsg", "Message table overflow. Either increase the value of Const MSG_ENTRIES or use ALL_MESSAGES instead of specific message values"
  786.           zAddMsg = False
  787.           GoTo Bail
  788.         End If
  789.         
  790.         zData(nCount, nBase) = uMsg                                            'Store the message in the appended table entry
  791.       End If
  792.     
  793.       zData(0, nBase) = nCount                                                 'Store the new table entry count
  794. Bail:
  795. End Function
  796.  
  797. 'Delete the message from the specified table of the window handle
  798. Private Sub zDelMsg(ByVal uMsg As Long, ByVal nTable As Long, ByVal z_ScMem As Long)
  799.       Dim nCount As Long                                                        'Table entry count
  800.       Dim nBase  As Long
  801.       Dim i      As Long                                                        'Loop index
  802.     
  803.       nBase = zData(nTable, z_ScMem)                                            'Map zData() to the specified table
  804.     
  805.       If uMsg = ALL_MESSAGES Then                                               'If ALL_MESSAGES are being deleted from the table...
  806.         zData(0, nBase) = 0                                                     'Zero the table entry count
  807.       Else
  808.         nCount = zData(0, nBase)                                                'Get the table entry count
  809.         
  810.         For i = 1 To nCount                                                     'Loop through the table entries
  811.           If zData(i, nBase) = uMsg Then                                        'If the message is found...
  812.             zData(i, nBase) = 0                                                 'Null the msg value -- also frees the element for re-use
  813.             GoTo Bail                                                           'Bail
  814.           End If
  815.         Next i                                                                  'Next message table entry
  816.         
  817.        ' zError "zDelMsg", "Message &H" & Hex$(uMsg) & " not found in table"
  818.       End If
  819. Bail:
  820. End Sub
  821.  
  822. '-The following routines are used for each of the three types of thunks ----------------------------
  823.  
  824. 'Maps zData() to the memory address for the specified thunk type
  825. Private Function zMap_VFunction(vFuncTarget As Long, _
  826.                                 vType As eThunkType, _
  827.                                 Optional oCallback As Object, _
  828.                                 Optional bIgnoreErrors As Boolean) As Long
  829.     
  830.     Dim thunkCol As Collection
  831.     Dim colID As String
  832.     Dim z_ScMem       As Long         'Thunk base address
  833.     
  834.     If vType = SubclassThunk Then
  835.         Set thunkCol = z_scFunk
  836.         colID = "h" & vFuncTarget
  837.     Else
  838.         zError "zMap_Vfunction", "Invalid thunk type passed"
  839.         Exit Function
  840.     End If
  841.     
  842.     If thunkCol Is Nothing Then
  843.         zError "zMap_VFunction", "Thunk hasn't been initialized"
  844.     Else
  845.         If thunkCol.Count Then
  846.             On Error GoTo Catch
  847.             z_ScMem = thunkCol(colID)               'Get the thunk address
  848.             If IsBadCodePtr(z_ScMem) Then z_ScMem = 0&
  849.             zMap_VFunction = z_ScMem
  850.         End If
  851.     End If
  852.     Exit Function                                               'Exit returning the thunk address
  853.     
  854. Catch:
  855.     ' error ignored when zUnThunk is called, error handled there
  856.     If Not bIgnoreErrors Then zError "zMap_VFunction", "Thunk type for " & vType & " does not exist"
  857. End Function
  858.  
  859. ' sets/retrieves data at the specified offset for the specified memory address
  860. Private Property Get zData(ByVal nIndex As Long, ByVal z_ScMem As Long) As Long
  861.   RtlMoveMemory VarPtr(zData), z_ScMem + (nIndex * 4), 4
  862. End Property
  863.  
  864. Private Property Let zData(ByVal nIndex As Long, ByVal z_ScMem As Long, ByVal nValue As Long)
  865.   RtlMoveMemory z_ScMem + (nIndex * 4), VarPtr(nValue), 4
  866. End Property
  867.  
  868. 'Error handler
  869. Private Sub zError(ByRef sRoutine As String, ByVal sMsg As String)
  870.   ' Note. These two lines can be rem'd out if you so desire. But don't remove the routine
  871. '  App.LogEvent TypeName(Me) & "." & sRoutine & ": " & sMsg, vbLogEventTypeError
  872. '  MsgBox sMsg & ".", vbExclamation + vbApplicationModal, "Error in " & TypeName(Me) & "." & sRoutine
  873. End Sub
  874.  
  875. 'Return the address of the specified DLL/procedure
  876. Private Function zFnAddr(ByVal sDLL As String, ByVal sProc As String, ByVal asUnicode As Boolean) As Long
  877.   If asUnicode Then
  878.     zFnAddr = GetProcAddress(GetModuleHandleW(StrPtr(sDLL)), sProc)         'Get the specified procedure address
  879.   Else
  880.     zFnAddr = GetProcAddress(GetModuleHandleA(sDLL), sProc)                 'Get the specified procedure address
  881.   End If
  882.   Debug.Assert zFnAddr                                                      'In the IDE, validate that the procedure address was located
  883.   ' ^^ FYI VB5 users. Search for zFnAddr("vba6", "EbMode") and replace with zFnAddr("vba5", "EbMode")
  884. End Function
  885.  
  886. 'Return the address of the specified ordinal method on the oCallback object, 1 = last private method, 2 = second last private method, etc
  887. Private Function zAddressOf(ByVal oCallback As Object, ByVal nOrdinal As Long) As Long
  888.     ' Note: used both in subclassing and hooking routines
  889.   Dim bSub  As Byte                                                         'Value we expect to find pointed at by a vTable method entry
  890.   Dim bVal  As Byte
  891.   Dim nAddr As Long                                                         'Address of the vTable
  892.   Dim i     As Long                                                         'Loop index
  893.   Dim J     As Long                                                         'Loop limit
  894.   
  895.   RtlMoveMemory VarPtr(nAddr), ObjPtr(oCallback), 4                         'Get the address of the callback object's instance
  896.   If Not zProbe(nAddr + &H1C, i, bSub) Then                                 'Probe for a Class method
  897.     If Not zProbe(nAddr + &H6F8, i, bSub) Then                              'Probe for a Form method
  898.       If Not zProbe(nAddr + &H710, i, bSub) Then                            'Probe for a PropertyPage method
  899.         If Not zProbe(nAddr + &H7A4, i, bSub) Then                          'Probe for a UserControl method
  900.             Exit Function                                                   'Bail...
  901.         End If
  902.       End If
  903.     End If
  904.   End If
  905.   
  906.   i = i + 4                                                                 'Bump to the next entry
  907.   J = i + 1024                                                              'Set a reasonable limit, scan 256 vTable entries
  908.   Do While i < J
  909.     RtlMoveMemory VarPtr(nAddr), i, 4                                       'Get the address stored in this vTable entry
  910.     
  911.     If IsBadCodePtr(nAddr) Then                                             'Is the entry an invalid code address?
  912.       RtlMoveMemory VarPtr(zAddressOf), i - (nOrdinal * 4), 4               'Return the specified vTable entry address
  913.       Exit Do                                                               'Bad method signature, quit loop
  914.     End If
  915.  
  916.     RtlMoveMemory VarPtr(bVal), nAddr, 1                                    'Get the byte pointed to by the vTable entry
  917.     If bVal <> bSub Then                                                    'If the byte doesn't match the expected value...
  918.       RtlMoveMemory VarPtr(zAddressOf), i - (nOrdinal * 4), 4               'Return the specified vTable entry address
  919.       Exit Do                                                               'Bad method signature, quit loop
  920.     End If
  921.     
  922.     i = i + 4                                                               'Next vTable entry
  923.   Loop
  924. End Function
  925.  
  926. '               o             by thsThunk
  927. 'End b                4                                           4      
  928. End Function
  929.  
  930. '               o             by thsThunk
  931. 'End b          ROC) = SS,Thunk)           'Ensure that the thunk hasn't already released its memory
  932.     If z_ScMem Then
  933.       Dim) = SS,                  signature, quit AC) = Sy - 1) = 0
  934.     oveMem thunEVPRng_hWnd,                     PRng2 4         M    5sn't al        mTits memor             _hWnd,                     PRng2 4         M    5sn't al           _h      D       PRng2 4   t As Long, _
  935.                          'Map zData    Dy      
  936.     If z_ScM thuAng = 12    'Thunk data index of the After table
  937.  method signaf the After table
  938.  method s   Ds8ScMNivEof t                    Dim thunkCol8pCODE_LEng_hWnd,      s Long) As Long
  939. C(a index of the After tabure, que...
  940.    
  941.     I1I      PR      &a Form methoong) Asaaen zUn dex of tA     bl(Alpentry counDl,f
  942.     abure, qu
  943. C(table overfl  PRng2 4   and r42/ a   bl(method sf the w Then
  944.       Exit Sub
  945.     End If
  946.   End If
  947.  
  948.   bLastShow = b of the Af        byrall  tA     bl(Alpentry     (nn oohe S
  949.  
  950.        VPRng_hWnd,             y co    bl(ub) T-defi   'Store the original WndPr                                                                 Ing =    data ind  nv9 bV        e...        'Null the       s L End If
  951.  
  952. Foop
  953. End Function
  954.  
  955. 0     PR      &a Form methoong) nd ivate index Mapthe Af        byrall  tA4If asUnic   I the w Then
  956.       Exit Sub
  957.     End If
  958.   En1(o
  959.   En1  &a Form methoong) nd ivate index Mapthe Af        byrall  tA4If asUnic   I +ys.tA4If asUUnic  '3yI +ys.tns.tA4"o
  960.                                                        tA            tubclas2_IngasUnic   I +yspti
  961. FoEIm          ubclassing cropertyng_hWTnature, quit ACzzzzass5     5zorm methos.tA4"o    n addreeeeeeeeeeeee  n as.tA z_ScMem'   D     Bump d If
  962. im i  c    users. S "Invhenect D7  users. S "aPsB55555555555555555555555555555555i2d signaf the Aft7rPtr(C8Fion
  963.  
  964. sDLL))    y co   
  965. End Function= zFnAdd6   
  966. End 
  967.   En ----ers. S "  
  968. E  
  969. Enders. S     dex * 4), V,     data ind d 
  970.   En ----ers.that tThen       M  e...vTheme),st &5of te),the ben                                    'TablTabW(0, nBf zFnAdd( tA           C      place with zFnA555555a                          tA            tubclas2_I Ds8ScMNivEof t          h_
  971. 'Endddddddddddddddddddddddddddddddddddlas2_I Ds8S72      S  C     IVATE  IVATE  I0ROC_OFF) 'SabSIf .cxgnature, quit loop
  972.     End If+'ben    Ereeeee. S     dex *eeeeeeezFnA555555a                   re that ng_hWVATE  oooooooo * 4), V, Bepce, vbInteg'((((((((((((1Dove thwD4I   
  973. End 
  974.   EnM6bbbbbbbbbbbbbbbbbe Aft7rPtr(C8   dex *eeeeeeezFnA5555t ad
  975.     End     yKh zFnA555555a                  rPtr(C8As Long2 = second   555t ad
  976.     End     yKh zFnA555555a                  rPtr(C8As DX_EBXpppppppppAs DX_6sVATE S6o"bbbbbbbe"X_6sVATE (ioooo rPtr(C8A4_6sVATE (ioooo rPl       
  977.   
  978. 'Nlised hereong2(       oo rPl       
  979.   arPtr(C8As4 oo rPl    EI data indes V, Bepce, vbInteg'((((((((((((1Dove thwD4I   
  980. End 
  981.   EnM6bbbbbEcounDM             
  982. End 
  983.   EnM6I      WP) = z
  984.     ata(I Long2 = second   555t as2_I Ds8
  985.  
  986. 'Bf zFnnAdd6 g2 = second   555t ad
  987.     End    ppppppppAs DXz:message toE to theen
  988.              _       rPtr(C8A4_6sVATE (ioooo rPl       
  989.   
  990. (
  991.     loop
  992.     End If+'EsVATE (io8
  993.  
  994. 'Bf zFnE to theen
  995.              _d   557N6
  996. (
  997. Ae ind         rPtr(C8AsnON  As Long C'    Ifn     sg      o       f the8) Then           1 ........0    EnzeeeeezFnAal WndProc
  998.    ar(Ct DDDDDDDDDDDndex of the After table(Ct DDDD                           'Bail...
  999.         End If
  1000.     f ALL_MEPR      &a Form      sVATE (ioooo rPl       
  1001. em + Izdata uelement for Ar      o       f the8) Then           1 ........0    EnzeeeeezFnAaoc
  1002.    (ioooo rPl   r     &a Form      sVATE (ioooo rPl       
  1003. em + Izdat72o rPl   r     &a Form      sVATE (ioooo rPl       
  1004. em        method
  1005.     Ifr(nValueElse
  1006.         zEr  Const IDX_FREE       As Long = 6   c
  1007.   th zFnA555555a 
  1008.   th zFnA555555a 
  1009.   th zFnA55555   aaaaaaaaa           to theenta indic  B                eenta ito theenta in1 ...d
  1010.     in1oooo rPl   zAddress ito       'Get the hodwo lines canSFnA5555ConsVATE (ioo3   o       f the8) Then ess     aaaaa         eef the8) Thenl       
  1011.   aef the8) Thenl       
  1012.   a canSFn'5  zAddf the8)'As Long,bbbef thess
  1013.    I data indes5555ConsVATE (iool   M          'Bump to -5vNVt as2_I Dsg Tcaaa     C      
  1014.   aef the      .......SPB                      'Ensure the new WndProc was set a canSFnwas set a canSFnwas setSs set      0#DWr("usereU
  1015.    rPl 7+e expe(ioooo rPl    anSFnwas setSs set           rPl 7+e eo -5vNVt as2_I Dsget       ) = zFnAdd
  1016.    6   c,unkdata)+1;  Exit Sub
  1017.     End If
  1018.   End If
  1019.  
  1020.   bLastShow = bShow
  1021. uu1as set a canSFnwas setM6I er tabsn't al   tong C6o 5vNVt a  tong C'Get the awas set       /      set7   aaaaa         eef the8) Thenlpter tab    f 5vNVt a  tong C'GWdinal * 41    ioo3   bject address i ) = zI er tabsnP 4), 48er tabsnP 4), 48er tabsnP 4), 48er tabsnP 4), 48er 'ering added 'Map zMB            'Bail...
  1022.      
  1023. End)     snP 4),7"End)oo rPl   whbject address8x$(uMs         C      place         'Bail...
  1024.      
  1025. End)     snP 4),7"End)oo rPl   whbjp'If
  1026.       't a&HFFFFFdnic 3<kCoil...e)zIbject a=Vt    toti           whbjectw           rPc 'eringIong C'GWdinal * tbjecOo'Bail...             txu  whbjecMa  snP 1e As LongEnd)oo rPl   whbjp'If
  1027.       't a&HFFFFFdnic 3,AM 4), 48erC45: zzzzzRetbjeccccccddddddddddddddddddddddddlimng C'GWd1e As LongEnd"keimng45:8er tabsnP 4), aaaaa      ...d
  1028.     in1oooo Long                                            nRe .cx, .Ync '   '2_I Dsgl method
  1029.   to 4), 48e  bl(A    'Bailtoti           whb     ee"keimaOs Long = 5     'Thunk dat5Fnwas secoo ,e
  1030. End Subsz) Thenl        _Sc(104) = &H81612445: z_Sc(105) = &H4C4&: z_tr(nAddr), i, 4                   'o_f
  1031.  
  1032.       I,   'Ensure the allocation succeeded
  1033.     
  1034.   End=I6Bump d If
  1035. im i  c    users. S "I index NhNVt aM1
  1036.  
  1037.   location succeeded
  1038.   p I,         i,<sE    tded
  1039.  nl        _S0thoA5555nteg'(((  plac9dddddddddddd  users ee"keimaOs Long = 5     '  557N6
  1040.     u     nP 4)Sate exeon succeeded
  1041.   p I,         i,<sE    tded
  1042.  ned
  1043.  ned
  1044.  
  1045.  ned
  1046.  
  1047.  neS5edddddduMsoA#en
  1048.    exeon succe* tbjecOo'Bailx NhNVt a      y cddddduMsoA#de API)CsCT7as2_IngasUnic   I oAs LongnedTo UBound(MecM t  y  allocation ss the   exeon succe*sCT7as2_IngasUnic   I oAs LongnedTo UBound(MeW) = zFnAddr("user32", "DestroyWindow", bUnicode)
  1049.       End IDE_LEng_ As Stris allocI)tance
  1050. As LongnedTo UBound(MecM t  yyyyyC64 oo   yKh zFC bSub As ng_ As Stris allocI)L
  1051.     Ifnction
  1052.  
  1053. '   ycond  indow h   ycond '       C bSub As ng_ As Stris allocI)L
  1054.     IWndPr  z        t  yyyyyC64 oo   pf      ction9 'eri  exeon succe*sCT7as2_IngasUcce*sCT8e awae with de API)CsCT7as2_IngasUnic   I oAs LongnedTo UBound(MecM t  y  allocation ss the   exeon succe*sCT7as2_IngasUnic   I oAs LongnedTo UBound(a'yC64 oo   yKh zFC bSub As ng_ As Stris allocI)L
  1055.     Ifnc= 5  B=Vt   uttabsnP a As LonIl nOrdiecM t '           5edTo U  Ifnc= 5 ) allocI)L
  1056.     Ifnc= 5  dex g_ As Stris adex unicode reqi63_Sc(34) t  yyySub As ng_ As'8em +ecM _6sVATxR     'Next message table entry
  1057.  s Stris adex unLUBound(a'yC64 oo   yKh   ctioenc= 5 7(ByVal nIndex AsS
  1058.         Selcx-ers.thaA#de 3or "zzzzzt   utdddddddqL   ctioenc=yKh   ctis DXz8 by the vvvvvvvvvv:5=Mnt to ALL_MEvvvvv:5=Mnb.tA4"o
  1059.    yyySub As ng              nRe .cx, .tA4"o
  1060.       n
  1061.   c  '3If+'bnMustoI 
  1062. End 
  1063.   EnM6bbbbb(Addr("user32", "DestroyWinur("u7.End 
  1064.   Sr      Selcx-eub As ng  _5   p 'lfinal)             'Get the addresbu'     he vvvvvvvvvv:5=Mnt to ALLs    S5C45: zzzzzRetbjeccccccddddddddddddddddddddddddlimng C'GWd1e As LongEnd"keimng45:8er tabsnP 4), aaaaa      ...d
  1065.     in1oooo Long                                            nRe .cx, .Ync '   '2_I Dsng            ddddddlimng C'w       next mesv:5=M_Sc(3LnRe .cx, .Yn -